#pragma once
#ifndef __SELECT_SVR_H__
#define __SELECT_SVR_H__

#include <iostream>
#include <string>
#include <vector>
#include <sys/select.h>
#include <sys/time.h>
#include "log.hpp"
#include "Sock.hpp"

#define NUM sizeof(fd_set)*8
#define FD_NONE -1

using namespace std;

class SelectServer
{
public:
    SelectServer(const uint16_t &port=8080)
        :_port(port)
    {
        _listensock=Sock::Socket();
        Sock::Bind(_listensock,_port);//ip缺省值为 0.0.0.0
        Sock::Listen(_listensock);
        logMessage(DEBUG,"%s","create base socket success");
        for(int i=0;i<NUM;i++)
        {
            _fd_array[i]=FD_NONE;
        }
        //做一个规定 _fd_array[0]=_listensock
        _fd_array[0]=_listensock;

    }
    void Start()
    {
        while(true)
        {
            DebugPrint();
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd=_listensock;
            for(int i=0;i<NUM;i++)
            {
                if(_fd_array[i]==FD_NONE) continue;
                FD_SET(_fd_array[i],&rfds);
                if(maxfd<_fd_array[i]) maxfd=_fd_array[i];
            }
            
            int n=select(maxfd+1,&rfds,nullptr,nullptr,nullptr);
            switch(n)
            {   
            case 0:
                logMessage(DEBUG,"time out...");
                break;
            case -1:  
                logMessage(WARNING,"select errno: %d : %s",errno,strerror(errno));
                break;
            default:
                //成功
                logMessage(DEBUG, "get a new link event..."); 
                HandlerEvent(rfds);

                break;  
            }

            // //int sock=Sock::Accept();
            // //struct timeval timeout={3,0};//放在里面是 因为当timeout的时间等待够3秒时，由于其为输入输出型参数，下次循环中的timeout会变为{0,0}
            // FD_SET(_listensock,&rfds);
            // //int n=select(_listensock+1,&rfds,nullptr,nullptr,&timeout);
            // int n=select(_listensock+1,&rfds,nullptr,nullptr,nullptr);
            // switch(n)
            // {
            // case 0:
            //     logMessage(DEBUG,"time out...");
            //     break;
            // case -1:  
            //     logMessage(WARNING,"select errno: %d : %s",errno,strerror(errno));
            //     break;
            // default:
            //     //成功
            //     break;  
            // }
        }
    }
    ~SelectServer()
    {
        if(_listensock>=0) close(_listensock);
    }

private:
    void Acceptr()
    {
        string clientip;
        uint16_t clientport=0;
        int sock=Sock::Accept(_listensock,&clientip,&clientport);
        if(sock<0)
        {
            logMessage(WARNING,"accept error");
            return;
        }
        logMessage(DEBUG,"get a new link success :[%s:%d] : %d",clientip.c_str(),clientport,sock);

        //找一个位置添加 我刚刚得到的sock套接字 好让select帮我关心
        int pos=1;
        for(;pos<NUM;pos++)
        {
            if(_fd_array[pos]==FD_NONE) break;
        }
        if(pos==NUM)
        {
            logMessage(WARNING,"%s:%d","select server already full,close fd: %d",sock);
            close(sock);
        }
        else
        {
            _fd_array[pos]=sock;
        }
    
    }
    void Recver(int pos)
    {
        // 读事件就绪：INPUT事件到来、recv，read
        logMessage(DEBUG, "message in, get IO event: %d", _fd_array[pos]);
        // 暂时先不做封装, 此时select已经帮我们进行了事件检测，fd上的数据一定是就绪的，即 本次 不会被阻塞
        // 这样读取有bug吗？有的，你怎么保证以读到了一个完整包文呢？
        char buffer[1024];
        int n = recv(_fd_array[pos], buffer, sizeof(buffer)-1, 0);
        if(n > 0)
        {
            buffer[n] = 0;
            logMessage(DEBUG, "client[%d]# %s", _fd_array[pos], buffer);
        }
        else if(n == 0)
        {
            logMessage(DEBUG, "client[%d] quit, me too...", _fd_array[pos]);
            // 1. 我们也要关闭不需要的fd
            close(_fd_array[pos]);
            // 2. 不要让select帮我关心当前的fd了
            _fd_array[pos] = FD_NONE;
        }
        else
        {
            logMessage(WARNING, "%d sock recv error, %d : %s", _fd_array[pos], errno, strerror(errno));
            // 1. 我们也要关闭不需要的fd
            close(_fd_array[pos]);
            // 2. 不要让select帮我关心当前的fd了
            _fd_array[pos] = FD_NONE;
        }
    }

    void HandlerEvent(const fd_set& rfds)
    {
        for(int i=0;i<NUM;i++)
        {
            //没让select关心这个文件
            if(_fd_array[i]==FD_NONE) continue; 

            //让关心了 但是需要知道他是否就绪
            if(FD_ISSET(_fd_array[i],&rfds))
            {
                //就绪
                //现在需要判断他是链接事件  accept
                //还是输入事件  recv read
                if(_fd_array[i]==_listensock)
                {
                    Acceptr();
                }
                else
                {
                    Recver(i);
                }
            }
        }
    }
    void DebugPrint()
    {
        cout<<"_fd_array[]: ";
        for(int i=0;i<NUM;i++)
        {
            if(_fd_array[i]==FD_NONE) continue;
            cout<<_fd_array[i]<<" ";
        }
        cout<<endl;
    }
private:
    uint16_t _port;
    int _listensock;
    int _fd_array[NUM];

};

#endif
